/* Copyright (c) 2010, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.logisim.circuit;

class SimulatorTicker extends Thread {
    private Simulator.PropagationManager manager;
    private int ticksPerTickPhase;
    private int millisPerTickPhase;

    private boolean shouldTick;
    private int ticksPending;
    private boolean complete;

    public SimulatorTicker(Simulator.PropagationManager manager) {
        this.manager = manager;
        ticksPerTickPhase = 1;
        millisPerTickPhase = 1000;
        shouldTick = false;
        ticksPending = 0;
        complete = false;
    }

    public synchronized void setTickFrequency(int millis, int ticks) {
        millisPerTickPhase = millis;
        ticksPerTickPhase = ticks;
    }

    synchronized void setAwake(boolean value) {
        shouldTick = value;
        if (shouldTick) {
            notifyAll();
        }

    }

    public synchronized void shutDown() {
        complete = true;
        notifyAll();
    }

    public synchronized void tickOnce() {
        ticksPending++;
        notifyAll();
    }

    @Override
    public void run() {
        long lastTick = System.currentTimeMillis();
        while (true) {
            boolean curShouldTick = shouldTick;
            int millis = millisPerTickPhase;
            int ticks = ticksPerTickPhase;
            try {
                synchronized(this) {
                    curShouldTick = shouldTick;
                    millis = millisPerTickPhase;
                    ticks = ticksPerTickPhase;
                    while (!curShouldTick && ticksPending == 0
                            && !complete) {
                        wait();
                        curShouldTick = shouldTick;
                        millis = millisPerTickPhase;
                        ticks = ticksPerTickPhase;
                    }
                }
            } catch (InterruptedException e) { }

            if (complete) {
                break;
            }


            int toTick;
            long now = System.currentTimeMillis();
            if (curShouldTick && now - lastTick >= millis) {
                toTick = ticks;
            } else {
                toTick = ticksPending;
            }

            if (toTick > 0) {
                lastTick = now;
                for (int i = 0; i < toTick; i++) {
                    manager.requestTick();
                }
                synchronized(this) {
                    if (ticksPending > toTick) {
                        ticksPending -= toTick;
                    }

                    else {
                        ticksPending = 0;
                    }

                }
                // we fire tickCompleted in this thread so that other
                // objects (in particular the repaint process) can slow
                // the thread down.
            }

            try {
                long nextTick = lastTick + millis;
                int wait = (int) (nextTick - System.currentTimeMillis());
                if (wait < 1) {
                    wait = 1;
                }

                if (wait > 100) {
                    wait = 100;
                }

                Thread.sleep(wait);
            } catch (InterruptedException e) { }
        }
    }
}
